#include <iostream>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <string.h>

using std::cout;
using std::endl;

#define ERR_CHECK(var, val, msg)                    \
    if (var == val)                                 \
    {                                               \
        std::cout << "error: " << msg << std::endl; \
        exit(-1);                                   \
    }

void server()
{
    int sockFd = socket(AF_INET, SOCK_STREAM, 0);

    sockaddr_in sAddr{}, cAddr{};
    sAddr.sin_addr.s_addr = inet_addr("0.0.0.0");
    sAddr.sin_family = AF_INET;
    sAddr.sin_port = htons(12345);

    int ret = bind(sockFd, (sockaddr *)&sAddr, sizeof(sAddr));
    ERR_CHECK(ret, -1, "bind");

    ret = listen(sockFd, 128);
    ERR_CHECK(ret, -1, "listen");

    epoll_event evt{}, ep[1024];
    int epfd = epoll_create(1);

    evt.data.fd = sockFd;
    evt.events = EPOLLIN;

    epoll_ctl(epfd, EPOLL_CTL_ADD, sockFd, &evt);
    int nready{};
    char buf[1024];
    while (1)
    {
        nready = epoll_wait(epfd, ep, 1024, -1);
        ERR_CHECK(nready, -1, "epoll_wait");

        for (size_t idx{}; idx < nready; idx++)
        {
            if (ep[idx].data.fd == sockFd)
            {
                socklen_t len = sizeof(cAddr);
                int clt = accept(sockFd, (sockaddr *)&cAddr, &len);
                evt.data.fd = clt;
                evt.events = EPOLLIN;
                epoll_ctl(epfd, EPOLL_CTL_ADD, clt, &evt);
                char *ip = inet_ntoa(cAddr.sin_addr);
                int port = ntohs(cAddr.sin_port);
                cout << "建立新连接, fd = " << clt << " ip = " << ip << ", port = " << port << endl;
            }
            else
            {
                cout << "--------------" << endl;
                int clt = ep[idx].data.fd;
                memset(buf, 0, strlen(buf));
                if ((read(clt, buf, sizeof(buf))) <= 0)
                {
                    cout << "断开连接..." << endl;
                    close(clt);
                    epoll_ctl(epfd, EPOLL_CTL_DEL, clt, NULL);
                }
                else
                {
                    for (size_t idx{}; idx < strlen(buf); idx++)
                    {
                        buf[idx] = toupper(buf[idx]);
                    }
                    write(clt, buf, strlen(buf));
                }
            }
        }
    }
    close(sockFd);
}

int main(int args, char *argv[])
{
    server();
    return 0;
}